﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows.Media.Imaging;
// MakePanoForGoogle, Copyright Mirosław M. Mączka.  All Rights Reserved. This code released under the terms of the Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)

namespace MakePanoForGoogle
{
    //===============================================================================================
    public class MetadataHandling
    {
        public static void WriteMetadata(string fileName, string description, DateTime dateTaken, double latitude, double longitude, double horizontalSpan, double verticalSpan, int imageWidth, int imageHeight, int poseHeadingDegrees)
        {
            bool withExiv = false;
            //
            //----------------------------------------------------------
            int CroppedAreaImageWidthPixels = imageWidth;
            int CroppedAreaImageHeightPixels = imageHeight;
            int FullPanoWidthPixels = (int)(imageWidth * (360.0 / horizontalSpan));
            //int FullPanoHeightPixels = (int)(imageHeight * (180.0 / verticalSpan));
            int FullPanoHeightPixels = FullPanoWidthPixels/2;
            int CroppedAreaLeftPixels = 0;
            //int CroppedAreaTopPixels = (FullPanoHeightPixels - CroppedAreaImageHeightPixels) / 2;
            int CroppedAreaTopPixels = (int)(((imageHeight * (180.0 / verticalSpan)) - CroppedAreaImageHeightPixels) / 2);
            //----------------------------------------------------------
            //
            // Oryginalny przykładowy kod z: http://rwlodarcmsdnblog.codeplex.com/SourceControl/latest#UsingInPlaceBitmapMetadataWriter/UsingInPlaceBitmapMetadataWriter.cs
            //
            const uint paddingAmount = 2048; // 2Kb padding for this example, but really this can be any value. 
            //
            string tmpFileName = System.IO.Path.GetRandomFileName();
            using (Stream originalFile = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                // lossless
                BitmapDecoder original = BitmapDecoder.Create(originalFile, BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.None);

                if (!original.CodecInfo.FileExtensions.Contains("jpg"))
                {
                    System.Windows.MessageBox.Show("The file you passed in is not a JPEG.", "WriteMetadata");
                    return;
                }
                JpegBitmapEncoder output = new JpegBitmapEncoder();
                if (original.Frames[0] != null && original.Frames[0].Metadata != null)
                {
                    BitmapMetadata metadata = original.Frames[0].Metadata.Clone() as BitmapMetadata;

                    metadata.SetQuery("/app1/ifd/PaddingSchema:Padding", paddingAmount);
                    metadata.SetQuery("/app1/ifd/exif/PaddingSchema:Padding", paddingAmount);
                    metadata.SetQuery("/xmp/PaddingSchema:Padding", paddingAmount);
                    //
                    if (ImageManipulations.IsResized)
                    {
                        metadata.SetQuery("/app1/ifd/{ushort=282}", rational(MainWindow.DPI, 1)); ;//XResolution
                        metadata.SetQuery("/app1/ifd/{ushort=283}", rational(MainWindow.DPI, 1));//YResolution
                        metadata.SetQuery("/app1/ifd/{ushort=296}", 2);//ResolutionUnit
                    }
                    // 
                    //----------------------------------------------------------
                    metadata.Title = description.Trim();
                    metadata.CameraModel = "ICE";
                    metadata.SetQuery("/app1/ifd/{ushort=305}", "MakePanoForGoogle");
                    //0x132 0x9003 0x9004
                    metadata.DateTaken = dateTaken.ToString("d");
                    metadata.Author = new System.Collections.ObjectModel.ReadOnlyCollection<string>(new List<string>() { Environment.UserName });
                    //----------------------------------------------------------
                    //metadata.SetQuery("/app1/ifd/gps/{ushort=0}", "2.2.0.0");
                    WriteNewTudeInImage(metadata, true, latitude);
                    WriteNewTudeInImage(metadata, false, longitude);
                    //----------------------------------------------------------
                    //
                    if (!withExiv)
                    {
                        metadata.SetQuery("/xmp/{wstr=http://ns.google.com/photos/1.0/panorama/}:UsePanoramaViewer", "True");
                        metadata.SetQuery("/xmp/{wstr=http://ns.google.com/photos/1.0/panorama/}:ProjectionType", "equirectangular");
                        metadata.SetQuery("/xmp/{wstr=http://ns.google.com/photos/1.0/panorama/}:CroppedAreaImageWidthPixels", CroppedAreaImageWidthPixels.ToString());
                        metadata.SetQuery("/xmp/{wstr=http://ns.google.com/photos/1.0/panorama/}:CroppedAreaImageHeightPixels", CroppedAreaImageHeightPixels.ToString());
                        metadata.SetQuery("/xmp/{wstr=http://ns.google.com/photos/1.0/panorama/}:FullPanoWidthPixels", FullPanoWidthPixels.ToString());
                        metadata.SetQuery("/xmp/{wstr=http://ns.google.com/photos/1.0/panorama/}:FullPanoHeightPixels", FullPanoHeightPixels.ToString());
                        metadata.SetQuery("/xmp/{wstr=http://ns.google.com/photos/1.0/panorama/}:CroppedAreaLeftPixels", CroppedAreaLeftPixels.ToString());
                        metadata.SetQuery("/xmp/{wstr=http://ns.google.com/photos/1.0/panorama/}:CroppedAreaTopPixels", CroppedAreaTopPixels.ToString());
                        metadata.SetQuery("/xmp/{wstr=http://ns.google.com/photos/1.0/panorama/}:PoseHeadingDegrees", poseHeadingDegrees.ToString());
                        metadata.SetQuery("/xmp/{wstr=http://ns.google.com/photos/1.0/panorama/}:StitchingSoftware", "ICE");
                    }
                    //----------------------------------------------------------

                    output.Frames.Add(BitmapFrame.Create(original.Frames[0], original.Frames[0].Thumbnail, metadata, original.Frames[0].ColorContexts));
                }

                using (Stream outputFile = File.Open(tmpFileName, FileMode.Create, FileAccess.ReadWrite))
                {
                    output.Save(outputFile);
                }
                originalFile.Close();
            }
            //
            //
            if(File.Exists(fileName))File.Delete(fileName);
            File.Move(tmpFileName, fileName);
            //
            if (withExiv)
            {
            //CroppedAreaImageWidthPixels        =  21253;
            //CroppedAreaImageHeightPixels       =  2352;
            //FullPanoWidthPixels                =  21253;
            //FullPanoHeightPixels               =  10626;
            //CroppedAreaLeftPixels              =  0;
            //CroppedAreaTopPixels               =  4137;
            //InitialHorizontalFOVDegrees = 248;
            poseHeadingDegrees = 0;

                //WriteXmpByExiv(fileName, CroppedAreaImageWidthPixels, CroppedAreaImageHeightPixels, FullPanoWidthPixels, FullPanoHeightPixels, CroppedAreaLeftPixels, CroppedAreaTopPixels, poseHeadingDegrees);
                WriteXmpByExiv(fileName, CroppedAreaImageWidthPixels, CroppedAreaImageHeightPixels, FullPanoWidthPixels, FullPanoHeightPixels, CroppedAreaLeftPixels, CroppedAreaTopPixels, poseHeadingDegrees);
            }
            //
        }
        //===============================================================================================
        public static void WriteNewTudeInImage(BitmapMetadata metadata, bool isLatitude, double tude)
        {
            //
            if (tude < 0)
            {
                tude = -tude;
                //
                if (isLatitude)
                {
                    metadata.SetQuery("/app1/ifd/gps/{ushort=1}", "S");
                }
                else
                {
                    metadata.SetQuery("/app1/ifd/gps/{ushort=3}", "W");
                }
            }
            else
            {
                if (isLatitude)
                {
                    metadata.SetQuery("/app1/ifd/gps/{ushort=1}", "N");
                }
                else
                {
                    metadata.SetQuery("/app1/ifd/gps/{ushort=3}", "E");
                }
            }

            //
            UInt32 iDegrees = (UInt32)tude;
            tude -= iDegrees;
            UInt32 iMinutes = (UInt32)(tude * 60.0);
            tude -= (iMinutes / 60.0);
            UInt32 iSeconds = (UInt32)(tude * 3600.0 * 1000);

            ulong[] vectorGps = new ulong[3];
            vectorGps[0] = rational(iDegrees, 1);
            vectorGps[1] = rational(iMinutes, 1);
            vectorGps[2] = rational(iSeconds, 1000);
            //
            //Mathematics.RationalNumbers.
            //
            if (isLatitude)
            {
                metadata.SetQuery("/app1/ifd/gps/{ushort=2}", vectorGps);
            }
            else
            {
                metadata.SetQuery("/app1/ifd/gps/{ushort=4}", vectorGps);
            }
            //
        }
        //===============================================================================================
        private static ulong rational(UInt32 value, uint denominator)
        {
            ulong numerator = value;
            return ((ulong)denominator) << 32 | numerator;
        }
        //===============================================================================================
        //===============================================================================================
        public static void WriteXmpByExiv(string panoramaFileName, int CroppedAreaImageWidthPixels, int CroppedAreaImageHeightPixels, int FullPanoWidthPixels, int FullPanoHeightPixels, int CroppedAreaLeftPixels, int CroppedAreaTopPixels, int poseHeadingDegrees)
        {
            //
            //
            //int fullPanoWidthPixels = (int)(imageWidth * 360.0 / horizontalSpan);
            //int fullPanoHeightPixels = (int)(imageHeight * 180.0 / verticalSpan);
            //
            //int croppedAreaTopPixels = (fullPanoHeightPixels - imageHeight) / 2;

            //int fullPanoHeightPixels = (int)(0.5 * imageWidth);
            //int croppedAreaTopPixels = (fullPanoHeightPixels - imageHeight) / 2;


            //
            string s = "";
            s += "# Exiv2 Google Photo Sphere command file" + Environment.NewLine;
            s += "# -------------------------" + Environment.NewLine;
            s += "#" + Environment.NewLine;
            s += "# $ exiv2 -m photo-sphere.txt file ..." + Environment.NewLine;
            s += Environment.NewLine;
            s += "reg GPano http://ns.google.com/photos/1.0/panorama/" + Environment.NewLine;
            s += Environment.NewLine;
            s += "set Xmp.GPano.UsePanoramaViewer XmpText True" + Environment.NewLine;
            s += "set Xmp.GPano.ProjectionType XmpText equirectangular" + Environment.NewLine;
            s += Environment.NewLine;

            s += string.Format("set Xmp.GPano.CroppedAreaImageWidthPixels XmpText {0}{1}", CroppedAreaImageWidthPixels, Environment.NewLine);
            s += string.Format("set Xmp.GPano.CroppedAreaImageHeightPixels XmpText {0}{1}", CroppedAreaImageHeightPixels, Environment.NewLine);
            s += Environment.NewLine;
            s += string.Format("set Xmp.GPano.FullPanoWidthPixels XmpText {0}{1}", FullPanoWidthPixels, Environment.NewLine);
            s += string.Format("set Xmp.GPano.FullPanoHeightPixels XmpText {0}{1}", FullPanoHeightPixels, Environment.NewLine);
            s += Environment.NewLine;
            s += string.Format("set Xmp.GPano.CroppedAreaLeftPixels XmpText {0}{1}", CroppedAreaLeftPixels, Environment.NewLine);
            s += string.Format("set Xmp.GPano.CroppedAreaTopPixels XmpText {0}{1}", CroppedAreaTopPixels, Environment.NewLine);
            s += Environment.NewLine;
            //
            //s += string.Format("set Xmp.GPano.PoseHeadingDegrees XmpText {0}{1}", poseHeadingDegrees, Environment.NewLine);
            s += string.Format("set Xmp.GPano.InitialHorizontalFOVDegrees XmpText {0}{1}", 90, Environment.NewLine);
            //s += string.Format("set Xmp.GPano.InitialHorizontalFOVDegrees XmpText {0}{1}", initialHorizontalFOVDegrees, Environment.NewLine);
            //s += string.Format("set Xmp.GPano.PoseHeadingDegrees XmpText {0}{1}", initialHorizontalFOVDegrees, Environment.NewLine);
            s += string.Format("set Xmp.GPano.StitchingSoftware XmpText {0}{1}", "ICE", Environment.NewLine);

            string cmdFilePathName = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "Exiv2.txt");
            //
            try
            {
                //
                if (System.IO.File.Exists(cmdFilePathName))
                {
                    System.IO.File.Delete(cmdFilePathName);
                }
                //
                System.IO.File.WriteAllText(cmdFilePathName, s);
                //
                System.Diagnostics.Process p = new System.Diagnostics.Process();
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.FileName = "Exiv2.Exe";
                p.StartInfo.Arguments = string.Format("-m \"{0}\" \"{1}\"", cmdFilePathName, panoramaFileName);
                p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
                p.Start();
                //
                p.WaitForExit();
                p.Close();
                //
            }
            catch (Exception e)
            {
                System.Windows.MessageBox.Show(e.Message, "RunExiv.Run");
            }

        }
        //===============================================================================================
    }//class
}//namespace
